package org.dreamwork.jasmine2.restful.client.gui.model;

import javax.swing.*;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.TableCellEditor;
import java.awt.*;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: seth.yang
 * Date: 2014/12/11
 * Time: 11:53
 */
public class CellEditors {
    public static final NumberCellEditor INTEGER_EDITOR = new NumberCellEditor (NumberCellEditor.TYPE_INTEGER);
    public static final NumberCellEditor FLOAT_EDITOR = new NumberCellEditor (NumberCellEditor.TYPE_FLOAT);
    public static final NumberCellEditor DATE_EDITOR = new NumberCellEditor (NumberCellEditor.TYPE_DATE);
    public static final BooleanCellEditor BOOLEAN_EDITOR = new BooleanCellEditor ();

    private static abstract class AbstractCelEditor implements TableCellEditor {
        protected java.util.List<CellEditorListener> listeners =
            Collections.synchronizedList (new ArrayList<CellEditorListener> ());

        @Override
        public boolean isCellEditable (EventObject e) {
            return true;
        }

        @Override
        public boolean shouldSelectCell (EventObject e) {
            return true;
        }

        @Override
        public boolean stopCellEditing () {
            fireEditStopped ();
            return true;
        }

        @Override
        public void cancelCellEditing () {

        }

        @Override
        public void addCellEditorListener (CellEditorListener l) {
            listeners.add (l);
        }

        @Override
        public void removeCellEditorListener (CellEditorListener l) {
            listeners.remove (l);
        }

        private void fireEditStopped () {
            ChangeEvent e = new ChangeEvent (this);
            Collection<CellEditorListener> c = new HashSet<CellEditorListener> ();
            synchronized (this) {
                c.addAll (listeners);
            }

            for (CellEditorListener l : c)
                l.editingStopped (e);
            c.clear ();
        }
    }

    public static class NumberCellEditor extends AbstractCelEditor {
        private JSpinner spinner;
        private int numberType;

        public static final int TYPE_INTEGER = 0, TYPE_FLOAT = 1;
        public static final int TYPE_DATE = 2;

        public NumberCellEditor (int numberType) {
            if (numberType == TYPE_INTEGER)
                spinner = new JSpinner ();
            else if (numberType == TYPE_FLOAT)
                spinner = new JSpinner (new SpinnerNumberModel (0.0, null, null, 0.01));
            else if (numberType == TYPE_DATE)
                spinner = new JSpinner (new SpinnerDateModel ());
            else
                spinner = new JSpinner ();
            this.numberType = numberType;
        }

        @Override
        public Component getTableCellEditorComponent (JTable table, Object value, boolean isSelected, int row, int column) {
            if (value == null) return spinner;

            if (this.numberType == TYPE_DATE) {
                if (value instanceof Date) {
                    spinner.setValue (value);
                } else if (value instanceof Number) {
                    spinner.setValue (new Date (((Number) value).longValue ()));
                } else {
                    try {
                        spinner.setValue (new Long (value.toString ()));
                    } catch (NumberFormatException ex) {
                        throw new IllegalArgumentException ("value must be a date or long object");
                    }
                }
            } else {
                if (value instanceof Number) {
                    spinner.setValue (value);
                } else {
                    try {
                        spinner.setValue (new BigDecimal ((String) value));
                    } catch (NumberFormatException ex) {
                        throw new IllegalArgumentException ("value must be a number");
                    }
                }
            }
            return spinner;
        }

        @Override
        public Object getCellEditorValue () {
            try {
                spinner.commitEdit ();
            } catch (ParseException e) {
                e.printStackTrace ();
            }
            return spinner.getValue ();
        }
    }

    public static class BooleanCellEditor extends AbstractCelEditor {
        private JCheckBox box = new JCheckBox ();
        @Override
        public Component getTableCellEditorComponent (JTable table, Object value, boolean isSelected, int row, int column) {
            Boolean b = false;
            if (value != null) {
                if (value instanceof Boolean) b = (Boolean) value;
                else
                    b = Boolean.valueOf (value.toString ());
            }

            box.setSelected (b);
            return box;
        }

        @Override
        public Object getCellEditorValue () {
            return box.isSelected ();
        }
    }
}